/*
 * Copyright 2021-2022 Open Kunlun Technology <https://www.openkunlun.io>
 */

package io.openkunlun.scalarpc.codegen.model

import scala.meta._

/**
 * an abstract method declaration
 *
 * @author kostas.kougios
 *         Date: 31/08/17
 */
case class DeclaredMethodEx private (
  meta: DeclaredMethodEx.Meta
) extends MethodEx[DeclaredMethodEx]
  with MetaEx.Contains
  with MetaEx.ContainsMods[DeclaredMethodEx] {

  override def tree: Decl.Def = Decl.Def(meta.mods.toList, meta.ename, meta.tparams.toList, meta.paramss.map(_.toList).toList, meta.tpe)

  override def name: String = meta.ename.value
  override def withName(name: String): DeclaredMethodEx = copy(meta = meta.copy(ename = meta.ename.copy(value = name)))

  override def parameters: Seq[Seq[TermParamEx]] = meta.paramss.map(_.map(p => TermParamEx(TermParamEx.Meta(p))))
  override def withParameters(params: Seq[Seq[TermParamEx]]): DeclaredMethodEx = copy(meta = meta.copy(paramss = params.map(_.map(_.meta.param).toList).toList))

  override def returnType: Option[TypeEx] = Some(TypeEx(meta.tpe))
  override def withReturnType(returnType: String): DeclaredMethodEx = copy(meta = meta.copy(tpe = returnType.parse[Type].get))

  override def implementation: Option[Term] = None
  override def withImplementation(expr: Term): DefinedMethodEx = DefinedMethodEx(DefinedMethodEx.Meta(meta.mods, meta.ename, meta.tparams, meta.paramss, Some(meta.tpe), expr))

  override def isAbstract: Boolean = true
  override def withAbstract: DeclaredMethodEx = this

  override def isOverrides: Boolean = meta.isOverrides
  override def withOverrides: DeclaredMethodEx = copy(meta = meta.copy(mods = Mod.Override() +: meta.mods))
  override def withMods(mods: ModsEx): DeclaredMethodEx = copy(meta = meta.copy(mods = mods.meta.mods.toList))
}

object DeclaredMethodEx extends PartialParser[DeclaredMethodEx] {
  override val parser: PartialFunction[Tree, DeclaredMethodEx] = {
    case q"..$mods def $ename[..$tparams](...$paramss): $tpe" =>
      DeclaredMethodEx(Meta(mods, ename, tparams, paramss, tpe))
  }

  case class Meta(
    mods: Seq[Mod],
    ename: Term.Name,
    tparams: Seq[scala.meta.Type.Param],
    paramss: Seq[Seq[Term.Param]],
    tpe: scala.meta.Type
  ) extends MetaEx with MetaEx.Mods

  def parseString(c: String): DeclaredMethodEx = parser(c.parse[Stat].get)
  def noArgReturningUnit(name: String): DeclaredMethodEx = DeclaredMethodEx(Meta(Nil, Term.Name(name), Nil, Nil, scala.meta.Type.Name("Unit")))
}